package com.peak.distribution.config;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.boot.autoconfigure.web.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.BasicErrorController;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
import org.springframework.boot.autoconfigure.web.ErrorViewResolver;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.peak.spring.boot.api.web.JsonMessage;
import com.peak.spring.boot.builder.web.JsonBuilder;
import com.peak.spring.boot.tools.Converts;
import com.peak.spring.boot.tools.Servlets;

import lombok.extern.slf4j.Slf4j;

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
@Slf4j
public class SpringErrorController extends AbstractErrorController {

	private final ErrorProperties errorProperties;

	/**
	 * Create a new {@link BasicErrorController} instance.
	 * @param errorAttributes the error attributes
	 * @param errorProperties configuration properties
	 */
	public SpringErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
		this(errorAttributes, errorProperties, Collections.<ErrorViewResolver>emptyList());
	}

	/**
	 * Create a new {@link BasicErrorController} instance.
	 * @param errorAttributes the error attributes
	 * @param errorProperties configuration properties
	 * @param errorViewResolvers error view resolvers
	 */
	public SpringErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
		super(errorAttributes, errorViewResolvers);
		Assert.notNull(errorProperties, "ErrorProperties must not be null");
		this.errorProperties = errorProperties;
	}

	@Override
	public String getErrorPath() {
		return this.errorProperties.getPath();
	}

	@RequestMapping(produces = "text/html")
	public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
		String ip = getIP(request);
		HttpStatus status = getStatus(request);
		Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
		response.setStatus(status.value());
		log.error("用户使用IP[{}]在[{}]的时候，请求[{}]时出现错误。{}", ip, Converts.toString(model.get("timestamp")), model.get("path"),
				model.get("error"));
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		// 这个错误页面的跳转好机智，默认直接找templates下面的error目录下面的根据状态命名的html
		return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
	}

	@RequestMapping
	@ResponseBody
	public ResponseEntity<JsonMessage> error(HttpServletRequest request, HttpServletResponse response) {
		String ip = getIP(request);
		Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
		HttpStatus status = getStatus(request);
		log.error("用户使用IP[{}]在[{}]的时候，请求[{}]时出现错误{}。\n{}", ip, Converts.toString(body.get("timestamp")), body.get("path"),
				body.get("trace"), body.get("error"));
		JsonMessage json = JsonBuilder.message().fail()
				.content(Converts.toString(body.get("error")) + ":" + Converts.toString(body.get("message"))).build();
		if (!HttpStatus.FORBIDDEN.equals(status)) {
			status = HttpStatus.OK;
		}
		return new ResponseEntity<JsonMessage>(json, status);
	}

	/**
	 * Determine if the stacktrace attribute should be included.
	 * 
	 * @param request the source request
	 * @param produces the media type produced (or {@code MediaType.ALL})
	 * @return if the stacktrace attribute should be included
	 */
	protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
		IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
		if (include == IncludeStacktrace.ALWAYS) {
			return true;
		}
		if (include == IncludeStacktrace.ON_TRACE_PARAM) {
			return getTraceParameter(request);
		}
		return false;
	}

	/**
	 * Provide access to the error properties.
	 * 
	 * @return the error properties
	 */
	protected ErrorProperties getErrorProperties() {
		return this.errorProperties;
	}

	protected String getIP(HttpServletRequest request) {
		String ip = null;
		try {
			ip = Servlets.getIp(request);
		} catch (Exception e2) {
			// 防止出现异常吧
		}
		return ip;
	}

}
